package org.nokatag.code;

import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.IndexColorModel;

public abstract class AbstractImageOp implements BufferedImageOp
{

public static final int EDGE_ZERO = 0;
public static final int EDGE_MIRROR = 1;
public static final int EDGE_CLAMP = 2;
protected int edgeMode;
protected RenderingHints hints;

public AbstractImageOp()
{
}

public int getEdgeMode()
{
    return edgeMode;
}

public void setEdgeMode(int edgeMode)
{
    this.edgeMode = edgeMode;
}

protected int getPixel(int pixels[], int x, int y, int width, int height, int edgeMode)
{
    if(x >= 0 && x < width && y >= 0 && y < height)
    {
        return pixels[x + y * width];
    }
    if(edgeMode == 0)
    {
        return 0;
    }
    if(edgeMode == 2)
    {
        if(x < 0)
        {
            x = 0;
        } else
        if(x >= width)
        {
            x = width - 1;
        }
        if(y < 0)
        {
            y = 0;
        } else
        if(y >= height)
        {
            y = height - 1;
        }
        return pixels[x + y * width];
    }
    if(x < 0)
    {
        x = -x;
    } else
    if(x >= width)
    {
        x = (width + width) - x - 1;
    }
    if(y < 0)
    {
        y = -y;
    } else
    if(y > height)
    {
        y = height = height - y - 1;
    }
    try{
    	return pixels[x + y * width];
    }catch(Exception se){
    	return 0;
    }
}

private int linear(int from, int to, int shift, double d)
{
    return (int)Math.floor((double)(from >> shift & 0xff) + d * (double)((to >> shift & 0xff) - (from >> shift & 0xff))) << shift;
}

private int linear(int from, int to, double d)
{
    int c = 0;
    for(int i = 0; i < 4; i++)
    {
        c += linear(from, to, i * 8, d);
    }

    return c;
}

protected int bilinear(int nw, int ne, int sw, int se, double xd, double yd)
{
    return linear(linear(nw, ne, xd), linear(sw, se, xd), yd);
}

protected int getPixelBilinear(int pixels[], double x, double y, int width, int height, 
        int edgeMode)
{
    int xi = (int)Math.floor(x);
    int yi = (int)Math.floor(y);
    double xd = x - (double)xi;
    double yd = y - (double)yi;
    int nw = getPixel(pixels, xi, yi, width, height, edgeMode);
    int ne = getPixel(pixels, xi + 1, yi, width, height, edgeMode);
    int sw = getPixel(pixels, xi, yi + 1, width, height, edgeMode);
    int se = getPixel(pixels, xi + 1, yi + 1, width, height, edgeMode);
    return bilinear(nw, ne, sw, se, xd, yd);
}

protected int limit(int v, int min, int max)
{
    if(v < min)
    {
        v = min;
    } else
    if(v > max)
    {
        v = max;
    }
    return v;
}

protected int limitByte(int v)
{
    return limit(v, 0, 255);
}

protected void filter(int ai[], int ai1[], int i, int j)
{
}

public BufferedImage filter(BufferedImage src, BufferedImage dest)
{
    if(dest == null)
    {
        dest = createCompatibleDestImage(src, null);
    }
    int width = src.getWidth();
    int height = src.getHeight();
    int inPixels[] = new int[width * height];
    int outPixels[] = new int[width * height];
    src.getRaster().getDataElements(0, 0, width, height, inPixels);
    filter(inPixels, outPixels, width, height);
    dest.getRaster().setDataElements(0, 0, width, height, outPixels);
    return dest;
}

public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM)
{
    if(destCM == null)
    {
        destCM = src.getColorModel();
        if(destCM instanceof IndexColorModel)
        {
            destCM = ColorModel.getRGBdefault();
        }
    }
    return new BufferedImage(destCM, destCM.createCompatibleWritableRaster(src.getWidth(), src.getHeight()), destCM.isAlphaPremultiplied(), null);
}

public Rectangle2D getBounds2D(BufferedImage src)
{
    return src.getRaster().getBounds();
}

public Point2D getPoint2D(Point2D srcPt, Point2D dstPt)
{
    if(dstPt == null)
    {
        dstPt = new java.awt.geom.Point2D.Float();
    }
    dstPt.setLocation(srcPt.getX(), srcPt.getY());
    return dstPt;
}

public RenderingHints getRenderingHints()
{
    return hints;
}
}

